#pragma once

#include <windows.h>
#include <gl\gl.h>
#include <gl\glu.h>
#include <vector>
#include <Core\Math\VectorT.hh>

using namespace OpenMesh;

enum RadiosityTexture
{
	Bj = 0,
	DeltaBj,
	Ij,
	DeltaIj,
	Rj,
	ItemBuffer,
	Array
};

enum Dimension
{
	Width = 0,
	Height
};

class Texture
{
public:
	Texture(GLenum target, GLenum proxyTarget);
	virtual ~Texture(void);
	void Create(const RadiosityTexture textureStyleSelection, const GLint internalFormat, const GLenum format, const GLenum type, int width, int height);
	void Create(const RadiosityTexture textureStyleSelection, const GLint internalFormat, const GLenum format, const GLenum type, int width, int height, const int depth);

	void RegenerateMipmap(void) const;
	const Vec2i GetTextureSize(void) const
	{
		return GetTextureSize(0);
	}

	const Vec2i GetTextureSize(int level) const
	{
		glPushAttrib(GL_TEXTURE_BIT);
		BindTexture();

		GLint width, height;
		glGetTexLevelParameteriv(_target, level, GL_TEXTURE_WIDTH, &width);
		glGetTexLevelParameteriv(_target, level, GL_TEXTURE_HEIGHT, &height);
		glPopAttrib();
		return Vec2i(width, height); 
	}

	const int GetTopLevel(void) const;

	void BindTexture() const;
	void BindTexture(const GLenum textureTarget) const;
	const GLenum GetTarget(void) const;
	virtual const GLuint GetReadId(void) const = 0;
protected:
	virtual void CallTexImage(const GLenum target, const GLint internalFormat, const GLsizei width, const GLsizei height, const GLsizei depth, const GLenum format, const GLenum type) = 0;

	std::vector<GLuint> _textureId;
	bool _swapTextures;
	const GLenum _target, _proxyTarget;
private:
	Texture& operator=(const Texture& other);
};

class Texture2D : public Texture
{
public:
	Texture2D(void);
	Texture2D(const RadiosityTexture textureStyleSelection, const GLint internalFormat, const GLenum format, const GLenum type, int width, int height);

	const GLuint GetReadId(void) const;
private:
	void CallTexImage(const GLenum target, const GLint internalFormat, const GLsizei width, const GLsizei height, const GLsizei depth, const GLenum format, const GLenum type);
	Texture2D(const Texture2D&);
	Texture2D& operator=(const Texture2D& other);
};

class Texture2DRW : public Texture
{
public:
	Texture2DRW(void);
	Texture2DRW(const RadiosityTexture textureStyleSelection, const GLint internalFormat, const GLenum format, const GLenum type, int width, int height);

	const GLuint GetReadId(void) const;
	const GLuint GetWriteId(void) const;

	void SwapReadWrite(void);
private:
	void CallTexImage(const GLenum target, const GLint internalFormat, const GLsizei width, const GLsizei height, const GLsizei depth, const GLenum format, const GLenum type);
	Texture2DRW(const Texture2DRW&);
	Texture2DRW& operator=(const Texture2DRW& other);
};

class TextureRectRW : public Texture
{
public:
	TextureRectRW(void);
	TextureRectRW(const RadiosityTexture textureStyleSelection, const GLint internalFormat, const GLenum format, const GLenum type, int width, int height);

	const GLuint GetReadId(void) const;
	const GLuint GetWriteId(void) const;

	void SwapReadWrite(void);
private:
	void CallTexImage(const GLenum target, const GLint internalFormat, const GLsizei width, const GLsizei height, const GLsizei depth, const GLenum format, const GLenum type);
	TextureRectRW(const TextureRectRW&);
	TextureRectRW& operator=(const TextureRectRW& other);
};

class TextureArray : public Texture
{
public:
	TextureArray(void);
	TextureArray(const RadiosityTexture textureStyleSelection, const GLint internalFormat, const GLenum format, const GLenum type, const int width, const int height, const int depth);

	const GLuint GetReadId(void) const;
private:
	void CallTexImage(const GLenum target, const GLint internalFormat, const GLsizei width, const GLsizei height, const GLsizei depth, const GLenum format, const GLenum type);
	TextureArray(const TextureArray&);
	TextureArray& operator=(const TextureArray& other);
};
